package datapack

import (
	"errors"
	"fmt"
	"miniDW/src/log"
	"strconv"
	"strings"
	"time"
)

const (
	FIELDTYPE_NUMBER         uint16 = 0x0102
	FIEKDTYPE_UNSIGNEDNUMBER uint16 = 0x0103
	FIELDTYPE_STRING         uint16 = 0x0104
	FIELDTYPE_TIMESTAMP      uint16 = 0x0105
	FIELDTYPE_FLOAT          uint16 = 0x0106
	FIELDTYPE_BIGINT         uint16 = 0x0107
	FIELDTYPE_UNSIGNEDBIGINT uint16 = 0x0108
	FIELDTYPE_INTEGER        uint16 = 0x0109
	FIELDTYPE_TEXT           uint16 = 0x0110
	FIELDTYPE_DATETIME       uint16 = 0x0111
)

type FieldType struct {
	Index       int
	Name        string
	Type        uint16
	Length      uint16
	FloatLength uint16
	ByteLength  int
}

func getFieldType(t uint16) string {
	switch t {
	case FIELDTYPE_BIGINT:
		return "bigint"
	case FIELDTYPE_UNSIGNEDBIGINT:
		return "bigint unsigned"
	case FIELDTYPE_NUMBER:
		return "number"
	case FIEKDTYPE_UNSIGNEDNUMBER:
		return "unsigned number"
	case FIELDTYPE_STRING:
		return "string"
	case FIELDTYPE_TIMESTAMP:
		return "timestamp"
	case FIELDTYPE_FLOAT:
		return "float"
	case FIELDTYPE_TEXT:
		return "text"
	case FIELDTYPE_DATETIME:
		return "datetime"
	default:
		return "unknown"
	}
}

func (f FieldType) Copy() *FieldType {
	newFieldType := f
	return &newFieldType
}

func (f FieldType) String() string {
	return fmt.Sprintf("[(%s) (%s) (%d, %d)]",
		f.Name,
		getFieldType(f.Type),
		f.Length,
		f.FloatLength)
}

type TableStruct struct {
	name      string
	columns   []string
	fieldsMap map[string]*FieldType
	condition *Condition
	sql       string
}

func NewTableStructByName(name string) *TableStruct {
	return &TableStruct{
		name:      name,
		columns:   make([]string, 0),
		fieldsMap: make(map[string]*FieldType),
	}
}

func (t *TableStruct) Copy() *TableStruct {
	dst := &TableStruct{
		name: t.name,
	}
	dst.columns = make([]string, len(t.columns))
	dst.fieldsMap = make(map[string]*FieldType, len(t.columns))
	for i, c := range t.columns {
		dst.columns[i] = c
		if t.fieldsMap != nil {
			dst.fieldsMap[c] = t.fieldsMap[c].Copy()
		}
	}
	if t.condition != nil {
		dst.condition = t.condition.Copy()
	}
	return dst
}

//func newTableStructBySql(sql string) (*TableStruct, error) {
//	fmt.Println(sql)
//	ts := &TableStruct{
//		Sql: sql,
//	}
//	stmt, err := sqlparser.Parse(sql)
//	if err != nil {
//		return nil, log.Error(err)
//	}
//	buffer := sqlparser.NewTrackedBuffer(nil)
//	stmt.Format(buffer)
//	fmt.Printf("stmt = %+v\n", reflect.TypeOf(stmt))
//	fmt.Printf("stmt = %s\n", buffer.String())
//	return ts, nil
//}

func (t *TableStruct) Name() string {
	return t.name
}

func (t *TableStruct) SetName(name string) {
	t.name = name
}

func (t *TableStruct) Sql() string {
	return t.sql
}

func NewTableStructByParam(param interface{}) (*TableStruct, error) {
	var err error
	ts := &TableStruct{}
	if a, ok := param.(map[string]interface{}); ok {
		if s, ok := a["sql"]; ok {
			if sql, ok := s.(string); ok {
				ts.sql = sql
			}
		}

		if name, ok := a["name"]; ok {
			ts.name = name.(string)
		}

		if b, ok := a["columns"]; ok {
			if cols, ok := b.([]interface{}); ok {
				ts.columns = make([]string, len(cols))
				for i, col := range cols {
					ts.columns[i] = col.(string)
				}
			}
		}
		if cond, ok := a["condition"]; ok {
			ts.condition, err = NewCondition(cond)
			if err != nil {
				return nil, err
			}
		}
		return ts, nil
	}
	return nil, log.Error(errors.New("table格式不正确"))
}

func (t *TableStruct) ClearFieldTypes() {
	t.fieldsMap = make(map[string]*FieldType, 0)
	t.columns = make([]string, 0)
}

func (t *TableStruct) AddFieldType(fieldType FieldType) error {
	if _, ok := t.fieldsMap[fieldType.Name]; ok {
		return log.Error(fmt.Errorf("duplicate field type: %s", fieldType.Name))
	}

	fieldType.Index = len(t.columns)
	t.columns = append(t.columns, fieldType.Name)
	t.fieldsMap[fieldType.Name] = &fieldType
	return nil
}

func (t *TableStruct) GetFeildsNames() []string {
	return t.columns
}

func (t *TableStruct) GetFieldTypeByName(name string) *FieldType {
	return t.fieldsMap[name]
}

func (t *TableStruct) GetConditionExpression() string {
	if t.condition == nil {
		return ""
	}
	return t.condition.String()
}

func (t *TableStruct) String() string {
	str := t.name + " : "
	if len(t.fieldsMap) > 0 {
		for _, field := range t.fieldsMap {
			str += fmt.Sprintf(" (%s) ", field)
		}
	}

	return str
}

func (t *TableStruct) Length() int {
	return len(t.columns)
}

func (t *TableStruct) ByteLength() int {
	length := 0
	for _, field := range t.fieldsMap {
		length += field.ByteLength
	}
	return length
}

func (t *TableStruct) Serialize(data []interface{}) (Row, error) {
	if len(data) != t.Length() {
		return nil, log.Error(fmt.Errorf("table has %d fields, expected %d", t.Length(), len(data)))
	}
	fieldCount := t.Length()
	rowData := make(Row, fieldCount)
	var col interface{}
	for i, field := range data {
		fieldInfo := t.GetFieldTypeByName(t.columns[i])
		if field != nil {
			switch fieldInfo.Type {
			case FIELDTYPE_INTEGER:
				col = field.(int64)
			case FIELDTYPE_BIGINT:
				col = field.(int64)
			case FIELDTYPE_UNSIGNEDBIGINT:
				col = field.(uint64)
			case FIELDTYPE_NUMBER,
				FIEKDTYPE_UNSIGNEDNUMBER:
				if num, err := strconv.Atoi(string(field.([]byte))); err == nil {
					col = num
				} else {
					log.Debugf("Failed to convert %v to int: %v", data, err)
				}
			case FIELDTYPE_TIMESTAMP:
				col = field.(time.Time)
			case FIELDTYPE_DATETIME:
				col = field.(time.Time)
			case FIELDTYPE_FLOAT:
				if num, err := strconv.ParseFloat(string(field.([]byte)), 64); err == nil {
					col = num
				} else {
					log.Debugf("Failed to convert %v to float: %v", data, err)
				}
			case FIELDTYPE_STRING:
				col = string(field.([]byte))
			case FIELDTYPE_TEXT:
				col = string(field.([]byte))
			default:
				col = field
			}
		} else {
			col = nil
		}
		rowData[i] = col
	}
	return rowData, nil
}

type Row []interface{}

func (r Row) String() string {
	str := make([]string, 0)
	for _, col := range r {
		str = append(str, fmt.Sprintf("%v", col))
	}
	return fmt.Sprintf("(%s)", strings.Join(str, ","))
}
